package tech.dort.dortware.impl.modules.exploit;

import com.google.common.eventbus.Subscribe;
import net.minecraft.block.Block;
import net.minecraft.block.BlockAir;
import net.minecraft.block.BlockHopper;
import net.minecraft.network.play.client.C03PacketPlayer;
import net.minecraft.util.AxisAlignedBB;
import net.minecraft.util.BlockPos;
import net.minecraft.util.MathHelper;
import tech.dort.dortware.api.module.Module;
import tech.dort.dortware.api.module.ModuleData;
import tech.dort.dortware.api.property.SliderUnit;
import tech.dort.dortware.api.property.impl.BooleanValue;
import tech.dort.dortware.api.property.impl.EnumValue;
import tech.dort.dortware.api.property.impl.NumberValue;
import tech.dort.dortware.api.property.impl.interfaces.INameable;
import tech.dort.dortware.impl.events.BlockCollisionEvent;
import tech.dort.dortware.impl.events.MovementEvent;
import tech.dort.dortware.impl.events.PacketEvent;
import tech.dort.dortware.impl.events.UpdateEvent;
import tech.dort.dortware.impl.utils.movement.ACType;
import tech.dort.dortware.impl.utils.movement.MotionUtils;
import tech.dort.dortware.impl.utils.networking.PacketUtil;

public class Phase extends Module {

    private final EnumValue<Mode> enumValue = new EnumValue<>("Mode", this, Phase.Mode.values());
    private final NumberValue distance = new NumberValue("Distance", this, 2F, 0.5F, 5, SliderUnit.BLOCKS);
    private final BooleanValue booleanValue = new BooleanValue("Sneak", this, true);

    public Phase(ModuleData moduleData) {
        super(moduleData);
        register(enumValue, distance, booleanValue);
    }

    // friend gave this
    public float getDirection() {
        float rotationYaw = mc.thePlayer.rotationYaw;
        if (mc.thePlayer.moveForward < 0F) rotationYaw += 180F;
        float forward = 1F;
        if (mc.thePlayer.moveForward < 0F) {
            forward = -0.5F;
        } else if (mc.thePlayer.moveForward > 0F) {
            forward = 0.5F;
        }
        if (mc.thePlayer.moveStrafing > 0F) rotationYaw -= 90F * forward;
        if (mc.thePlayer.moveStrafing < 0F) rotationYaw += 90F * forward;
        return (float) Math.toRadians(rotationYaw);
    }

    @Subscribe
    public void onPacket(PacketEvent packetEvent) {
        if (enumValue.getValue() == Mode.AAC) {
            if (packetEvent.getPacket() instanceof C03PacketPlayer) {
                C03PacketPlayer packetPlayer = packetEvent.getPacket();
                float yaw = mc.thePlayer.rotationYaw;
                packetPlayer.x = (packetPlayer.x - Math.sin(yaw) * 0.00000001D);
                packetPlayer.z = (packetPlayer.z + Math.cos(yaw) * 0.00000001D);
            }
        }
    }

    @Subscribe
    public void onUpdate(UpdateEvent event) {
        try {
            Mode mode = enumValue.getValue();
            double[] forward = MotionUtils.teleportForward(distance.getValue());
            if (booleanValue.getValue()) {
                if (mc.thePlayer.isSneaking()) {
                    switch (mode) {
                        case AAC:
                            mc.thePlayer.noClip = true;
                            double yaw = Math.toRadians(mc.thePlayer.rotationYaw);
                            double s = 0.00001873;//mc.thePlayer.isMoving() ? mc.thePlayer.ticksExisted % 3 == 0 ? .00010000999956 : 0.021 : 0;
                            mc.thePlayer.setPosition(mc.thePlayer.posX + s * -Math.sin(yaw), mc.thePlayer.posY + 1.0E-9, mc.thePlayer.posZ + s * Math.cos(yaw));
                            break;
                        case VERTICAL:
                            if (mc.thePlayer.isCollidedHorizontally) {
                                PacketUtil.sendPacketNoEvent(new C03PacketPlayer.C06PacketPlayerPosLook(mc.thePlayer.posX + forward[0] / 4, mc.thePlayer.posY, mc.thePlayer.posZ + forward[1] / 4, mc.thePlayer.rotationYaw + 1, mc.thePlayer.rotationPitch + 1, true));
                                PacketUtil.sendPacketNoEvent(new C03PacketPlayer.C06PacketPlayerPosLook(mc.thePlayer.posX, mc.thePlayer.posY + 11, mc.thePlayer.posZ, mc.thePlayer.rotationYaw - 1, mc.thePlayer.rotationPitch - 1, false));
                                mc.thePlayer.setPosition(mc.thePlayer.posX + forward[0] / 4, mc.thePlayer.posY, mc.thePlayer.posZ + forward[1] / 4);
                            }

                            if (isInsideBlock()) {
                                MotionUtils.setMotion(MotionUtils.getBaseSpeed(ACType.HYPIXEL) / 2);
                            }
                            break;
                        case PACKETLESS:
                            if (mc.thePlayer.isCollidedHorizontally)
                                mc.thePlayer.setPosition(mc.thePlayer.posX + forward[0] / 4, mc.thePlayer.posY, mc.thePlayer.posZ + forward[1] / 4);
                            break;
                        case SKIP:
                            if (mc.thePlayer.isCollidedHorizontally) {
                                PacketUtil.sendPacketNoEvent(new C03PacketPlayer.C04PacketPlayerPosition(mc.thePlayer.posX + MotionUtils.teleportForward(0.005F)[0], mc.thePlayer.posY, mc.thePlayer.posZ + MotionUtils.teleportForward(0.005F)[1], true));
                                mc.thePlayer.setPosition(mc.thePlayer.posX + MotionUtils.teleportForward(0.005F)[0], mc.thePlayer.posY, mc.thePlayer.posZ + MotionUtils.teleportForward(0.005F)[1]);
                            }

                            if (isInsideBlock()) {
                                PacketUtil.sendPacketNoEvent(new C03PacketPlayer.C04PacketPlayerPosition(mc.thePlayer.posX + 1.5 * Math.cos(Math.toRadians(mc.thePlayer.rotationYaw + 90.0F)), mc.thePlayer.posY, mc.thePlayer.posZ + 1.5 * Math.sin(Math.toRadians(mc.thePlayer.rotationYaw + 90.0F)), true));
                                mc.thePlayer.boundingBox.offsetAndUpdate(1.5 * Math.cos(Math.toRadians(mc.thePlayer.rotationYaw + 90.0F)), 0.0D, 1.5 * Math.sin(Math.toRadians(mc.thePlayer.rotationYaw + 90.0F)));
                            }
                            break;
                        case VOID:
                            if (mc.thePlayer.isCollidedHorizontally) {
                                PacketUtil.sendPacketNoEvent(new C03PacketPlayer.C04PacketPlayerPosition(mc.thePlayer.posX + forward[0], mc.thePlayer.posY, mc.thePlayer.posZ + forward[1], true));
                                PacketUtil.sendPacketNoEvent(new C03PacketPlayer.C04PacketPlayerPosition(mc.thePlayer.posX + forward[0], mc.thePlayer.posY - 256, mc.thePlayer.posZ + forward[1], true));
                                mc.thePlayer.setPosition(mc.thePlayer.posX + forward[0], mc.thePlayer.posY, mc.thePlayer.posZ + forward[1]);
                            }
                            break;
                        case OLD:
                            if (mc.thePlayer.isCollidedHorizontally) {
                                PacketUtil.sendPacketNoEvent(new C03PacketPlayer.C04PacketPlayerPosition(mc.thePlayer.posX + forward[0], mc.thePlayer.posY, mc.thePlayer.posZ + forward[1], true));
                                PacketUtil.sendPacketNoEvent(new C03PacketPlayer.C04PacketPlayerPosition(mc.thePlayer.posX + forward[0], mc.thePlayer.posY - 256, mc.thePlayer.posZ + forward[1], true));
                                mc.thePlayer.setPosition(mc.thePlayer.posX + forward[0], mc.thePlayer.posY, mc.thePlayer.posZ + forward[1]);
                            }

                            if (isInsideBlock()) {
                                event.setOnGround(true);
                                MotionUtils.setMotion(distance.getValue());
                                mc.thePlayer.motionY = mc.thePlayer.movementInput.jump ? distance.getValue() / 2 : mc.thePlayer.movementInput.sneak ? -distance.getValue() / 2 : 0;
//                            } else if (mc.thePlayer.movementInput.sneak) {
//                                PacketUtil.sendPacketNoEvent(new C03PacketPlayer.C04PacketPlayerPosition(mc.thePlayer.posX, mc.thePlayer.posY + 256, mc.thePlayer.posZ, true));
//                                PacketUtil.sendPacketNoEvent(new C03PacketPlayer.C04PacketPlayerPosition(mc.thePlayer.posX, mc.thePlayer.posY, mc.thePlayer.posZ, true));
//                                mc.thePlayer.setPosition(mc.thePlayer.posX, mc.thePlayer.posY - 0.0625, mc.thePlayer.posZ);
                            }
                            break;
                    }
                }
            } else {
                switch (mode) {
                    case AAC:
                        mc.thePlayer.noClip = true;
                        double yaw = Math.toRadians(mc.thePlayer.rotationYaw);
                        double s = 0.00001873;//mc.thePlayer.isMoving() ? mc.thePlayer.ticksExisted % 3 == 0 ? .00010000999956 : 0.021 : 0;
                        mc.thePlayer.setPosition(mc.thePlayer.posX + s * -Math.sin(yaw), mc.thePlayer.posY + 1.0E-9, mc.thePlayer.posZ + s * Math.cos(yaw));
                        break;
                    case VERTICAL:
                        if (mc.thePlayer.isCollidedHorizontally) {
                            PacketUtil.sendPacketNoEvent(new C03PacketPlayer.C06PacketPlayerPosLook(mc.thePlayer.posX + forward[0] / 4, mc.thePlayer.posY, mc.thePlayer.posZ + forward[1] / 4, mc.thePlayer.rotationYaw + 1, mc.thePlayer.rotationPitch + 1, true));
                            PacketUtil.sendPacketNoEvent(new C03PacketPlayer.C06PacketPlayerPosLook(mc.thePlayer.posX, mc.thePlayer.posY + 11, mc.thePlayer.posZ, mc.thePlayer.rotationYaw - 1, mc.thePlayer.rotationPitch - 1, false));
                            mc.thePlayer.setPosition(mc.thePlayer.posX + forward[0] / 4, mc.thePlayer.posY, mc.thePlayer.posZ + forward[1] / 4);
                        }

                        if (isInsideBlock()) {
                            MotionUtils.setMotion(MotionUtils.getBaseSpeed(ACType.HYPIXEL) / 2);
                        }
                        break;
                    case PACKETLESS:
                        if (mc.thePlayer.isCollidedHorizontally)
                            mc.thePlayer.setPosition(mc.thePlayer.posX + forward[0] / 4, mc.thePlayer.posY, mc.thePlayer.posZ + forward[1] / 4);
                        break;
                    case SKIP:
                        if (mc.thePlayer.isCollidedHorizontally) {
                            PacketUtil.sendPacketNoEvent(new C03PacketPlayer.C04PacketPlayerPosition(mc.thePlayer.posX + MotionUtils.teleportForward(0.005F)[0], mc.thePlayer.posY, mc.thePlayer.posZ + MotionUtils.teleportForward(0.005F)[1], true));
                            mc.thePlayer.setPosition(mc.thePlayer.posX + MotionUtils.teleportForward(0.005F)[0], mc.thePlayer.posY, mc.thePlayer.posZ + MotionUtils.teleportForward(0.005F)[1]);
                        }

                        if (isInsideBlock()) {
                            PacketUtil.sendPacketNoEvent(new C03PacketPlayer.C04PacketPlayerPosition(mc.thePlayer.posX + 1.5 * Math.cos(Math.toRadians(mc.thePlayer.rotationYaw + 90.0F)), mc.thePlayer.posY, mc.thePlayer.posZ + 1.5 * Math.sin(Math.toRadians(mc.thePlayer.rotationYaw + 90.0F)), true));
                            mc.thePlayer.boundingBox.offsetAndUpdate(1.5 * Math.cos(Math.toRadians(mc.thePlayer.rotationYaw + 90.0F)), 0.0D, 1.5 * Math.sin(Math.toRadians(mc.thePlayer.rotationYaw + 90.0F)));
                        }
                        break;
                    case VOID:
                        if (mc.thePlayer.isCollidedHorizontally) {
                            PacketUtil.sendPacketNoEvent(new C03PacketPlayer.C04PacketPlayerPosition(mc.thePlayer.posX + forward[0], mc.thePlayer.posY, mc.thePlayer.posZ + forward[1], true));
                            PacketUtil.sendPacketNoEvent(new C03PacketPlayer.C04PacketPlayerPosition(mc.thePlayer.posX + forward[0], mc.thePlayer.posY - 256, mc.thePlayer.posZ + forward[1], true));
                            mc.thePlayer.setPosition(mc.thePlayer.posX + forward[0], mc.thePlayer.posY, mc.thePlayer.posZ + forward[1]);
                        }
                        break;
                    case OLD:
                        if (mc.thePlayer.isCollidedHorizontally) {
                            PacketUtil.sendPacketNoEvent(new C03PacketPlayer.C04PacketPlayerPosition(mc.thePlayer.posX + forward[0], mc.thePlayer.posY, mc.thePlayer.posZ + forward[1], true));
                            PacketUtil.sendPacketNoEvent(new C03PacketPlayer.C04PacketPlayerPosition(mc.thePlayer.posX + forward[0], mc.thePlayer.posY - 256, mc.thePlayer.posZ + forward[1], true));
                            mc.thePlayer.setPosition(mc.thePlayer.posX + forward[0], mc.thePlayer.posY, mc.thePlayer.posZ + forward[1]);
                        }

                        if (isInsideBlock()) {
                            event.setOnGround(true);
                            MotionUtils.setMotion(distance.getValue());
                            mc.thePlayer.motionY = mc.thePlayer.movementInput.jump ? distance.getValue() / 2 : mc.thePlayer.movementInput.sneak ? -distance.getValue() / 2 : 0;
//                            } else if (mc.thePlayer.movementInput.sneak) {
//                                PacketUtil.sendPacketNoEvent(new C03PacketPlayer.C04PacketPlayerPosition(mc.thePlayer.posX, mc.thePlayer.posY + 256, mc.thePlayer.posZ, true));
//                                PacketUtil.sendPacketNoEvent(new C03PacketPlayer.C04PacketPlayerPosition(mc.thePlayer.posX, mc.thePlayer.posY, mc.thePlayer.posZ, true));
//                                mc.thePlayer.setPosition(mc.thePlayer.posX, mc.thePlayer.posY - 0.0625, mc.thePlayer.posZ);
                        }
                        break;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private boolean isInsideBlock() {
        try {
            for (int x = MathHelper.floor_double(
                    mc.thePlayer.getEntityBoundingBox().minX); x < MathHelper.floor_double(mc.thePlayer.getEntityBoundingBox().maxX) + 1; ++x) {
                for (int y = MathHelper.floor_double(
                        mc.thePlayer.getEntityBoundingBox().minY + 1.0D); y < MathHelper.floor_double(mc.thePlayer.getEntityBoundingBox().maxY)
                        + 2; ++y) {
                    for (int z = MathHelper.floor_double(
                            mc.thePlayer.getEntityBoundingBox().minZ); z < MathHelper.floor_double(mc.thePlayer.getEntityBoundingBox().maxZ)
                            + 1; ++z) {
                        Block block = mc.theWorld.getBlockState(new BlockPos(x, y, z)).getBlock();
                        if (block != null && !(block instanceof BlockAir)) {
                            AxisAlignedBB boundingBox = block.getCollisionBoundingBox(mc.theWorld, new BlockPos(x, y, z),
                                    mc.theWorld.getBlockState(new BlockPos(x, y, z)));
                            if (block instanceof BlockHopper) {
                                boundingBox = new AxisAlignedBB(x, y, z, x + 1, y + 1, z + 1);
                            }

                            if (boundingBox != null && mc.thePlayer.getEntityBoundingBox().intersectsWith(boundingBox))
                                return true;
                        }
                    }
                }
            }
            return false;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    @Subscribe
    public void onCollide(BlockCollisionEvent event) {
        try {
            if (!booleanValue.getValue() || mc.thePlayer.isSneaking() || enumValue.getValue() == Mode.SAND) {
                switch (enumValue.getValue()) {
                    case SAND:
                        if (isInsideBlock()) {
                            event.setAxisAlignedBB(null);
                            mc.thePlayer.noClip = true;
                        }
                        break;

                    case SKIP:
                    case OLD:
                    case AAC:
                        if (event.getY() >= mc.thePlayer.posY) {
                            event.setAxisAlignedBB(null);
                            mc.thePlayer.noClip = true;
                        }
                        break;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Subscribe
    public void onMove(MovementEvent event) {
        if (enumValue.getValue().equals(Mode.SAND) && isInsideBlock()) {
            MotionUtils.setMotion(event, distance.getValue());
            mc.thePlayer.motionY = 0;
            event.setMotionY(mc.thePlayer.movementInput.jump ? distance.getValue() : mc.thePlayer.movementInput.sneak ? -distance.getValue() : 0);
        }
    }

    @Override
    public void onDisable() {
        super.onDisable();
        mc.thePlayer.noClip = false;
    }

    public enum Mode implements INameable {
        SKIP("Skip"), SAND("Sand"), AAC("AAC"), VOID("Void"), VERTICAL("Hypixel"), PACKETLESS("Packetless"), OLD("Old");
        private final String name;

        Mode(String name) {
            this.name = name;
        }

        @Override
        public String getDisplayName() {
            return name;
        }
    }
}
